/* eslint-disable */
class Enum {

    constructor(value) {
        this.value = value
    }
}

class MetaObject {

    constructor() {
        let t = this.__type__
        t.__properties__.forEach((v, k) => {
            console.log(v === String)
            this[k] = null
        })
    }

    static makeType(name) {
        let result = class extends MetaObject {
            get __type__() {
                return result
            }
        }
        result['__name__'] = name
        result['__fields__'] = new Map()
        result['__properties__'] = new Map()
        return result
    }

    static makeArray() {

        const result = class extends Array {
            get type() {
                return result
            }

            constructor(...items) {
                super(...items)
            }
        }

        // Object.defineProperty(result, "type", { get: function(){return result}})

        return result

    }

}

class MetadataProvider {

    types = new Map()

    construct() {

    }

    get_type(id) {
        return this.types[id]
    }

    _add_type(typeinfo) {
        this.types[typeinfo.name] = typeinfo
    }

    _make_type(id) {
        let result = this.get_type(id)
        if (!result) {
            result = MetaObject.makeType(id)
            this.types[id] = result
        }
        return result
    }

    _make_array(id) {
        let result = this.get_type(`${id}[]`)
        if (!result) {
            this._make_type(id)
            result = Array//array(str('u'), id)
        }
        return result
    }

    _make_enum(id, flags) {
        let result = this.get_type(id)
        if (!result) {
            result = class extends Enum {
                constructor(value) {
                    super(value)
                }
                get __type__() {
                    return result
                }
            }
            result['__flags__'] = flags
            result['__name__'] = id
        }
        return result
    }

    add_types(typedef) {

        throw new Error("not implemented error");
    }


    get_objects(objectdef) {
        throw new Error("not implemented error");
    }

}

export class JsonMetadataProvider extends MetadataProvider {

    constructor() {
        super()
        this.types['str'] = String
        this.types['int'] = Number
        this.types['double'] = Number
        this.types['bool'] = Boolean
        this.types['object'] = Object
    }

    __visit_type(id, value) {
        let valuetype = value['type']
        if (valuetype === 'object') {
            let typeinfo = this._make_type(id)
            let properties = value.properties || []
            properties.forEach(p => {
                let propertyname = `${p['id']}`
                let property = this.__visit_property(typeinfo, propertyname, p)
                typeinfo.__properties__.set(propertyname, property)
            })
            return typeinfo
        }
        else if (valuetype === 'array') {
            let item = value['items']
            let arraytype = this.__visit_type(item['type'], item)
            return this._make_array(arraytype.__name__)
        }
        else if (valuetype === 'ref') {
            const refid = value['refId'] || 'object'
            return this._make_type(`${refid}`)
        }
        else if (valuetype === 'number') {
            let format = value['format'] || 'double'
            return this.get_type(format)
        }
        else if (valuetype === 'text') {
            return this.get_type('str')
        }
        else if (valuetype == 'enum') {
            let flags = new Map()
            for (const x in value['enum']) {
                flags.set(`${x['value']}`, x['value'])
            }
            // let name = 'enum'+''.join(flags.keys())
            return this._make_enum('enum', flags)
        }
        else if (valuetype == 'bool') {
            return this.get_type('bool')
        }
        else {
            throw new Error('invalid type value')
        }
    }

    __visit_property(type, id, value) {
        let valuetype = value['type']
        if (valuetype === 'object') {
            valuetype = this.__visit_type(
                `${type.__name__}_${id}`, value)
            return valuetype
        }
        else {
            return this.__visit_type(valuetype, value)
        }
    }

    __visit_field(id, value) {
        //TODO: field
        throw new Error("not impl")
    }

    add_types(typedef) {
        typedef.forEach(item => {
            const typeinfo = this.__visit_type(`${item['id']}`, item)
            this.types[typeinfo.__name__] = typeinfo
        })
    }

    get_objects(objectdef) {
        let refid = objectdef['_refId']
        let typename = `${refid}`
        let typeinfo = this.get_type(typename)
        return this.__visit_object(typeinfo, objectdef)
    }

    __visit_object(typeinfo, value) {
        let result = new typeinfo()
        typeinfo.__properties__.forEach((type, name) => {
            let property = value[name]
            if (property) {
                if (type.prototype instanceof MetaObject) {
                    result[name] = this.__visit_object(type, property)
                }
            }
            result[name] = this.__visit_value(type, property)
        })
        return result
    }

    __visit_value(valuetype, value) {
        if (valuetype instanceof Array) {
            return value
        }
        if (valuetype.prototype instanceof Enum) {
            return new valuetype(value)
        }
        else {
            return value
        }
    }

}




export default new JsonMetadataProvider();


// let provider = new JsonMetadataProvider()
// var fs = require('fs');
// const { type } = require('os')

// let type_data = JSON.parse(fs.readFileSync('./type.json', 'utf8'));

// const typeData = `[{ "id": 2, "type": "object", "properties": [{ "id": "title", "type": "text" }, { "id": "path", "type": "text" }, { "id": "caption", "type": "text" }, { "id": "format", "enum": [{ "value": "rzTimeseriesSpec" }, { "value": "rzTimeseriesTrend" }], "type": "enum" }, { "id": "match", "type": "bool" }] }, { "id": 5, "type": "object", "properties": [{ "id": "key", "type": "text" }, { "id": "title", "type": "text" }, { "id": "tag", "type": "text" }, { "id": "data", "type": "ref", "refId": 6 }, { "id": "children", "type": "array", "items": { "type": "ref", "refId": 5 } }] }, { "id": 6, "type": "object", "properties": [{ "id": "id", "type": "text" }, { "id": "path", "type": "text" }, { "id": "remark", "type": "text" }, { "id": "format", "enum": [{ "value": "rzTimeseriesSpec" }, { "value": "rzTimeseriesTrend" }], "type": "enum" }, { "id": "type", "type": "text" }, { "id": "sampleRate", "type": "number", "format": "double" }, { "id": "convertCoef", "type": "number", "format": "double" }, { "id": "measDate", "type": "number", "format": "int" }, { "id": "value", "type": "number", "format": "double" }, { "id": "trendPath", "type": "array", "items": { "type": "ref", "refId": 2 } }] }]`
// provider.add_types(type_data)

// provider.add_types(JSON.parse(typeData))

// let T = provider.get_type(452)
// ttt = new T()

// let T2 = provider.get_type(5)
// ttt2 = new T2()

// let object_data = JSON.parse(fs.readFileSync('./object.json', 'utf8'));
// obj = provider.get_objects(object_data)



// t = obj.Components.Generator.Components.FrontBearing.Basic.type
// v = obj.Components.Generator.Components.FrontBearing.Basic.BearingForm
// console.log(v);
